package com.zzl.springboot_mybatis_demo.aspect;

import com.gitee.yongzhuzl.commonutil.util.array.ArrayUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.javassist.ClassClassPath;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.javassist.bytecode.CodeAttribute;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.apache.ibatis.javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * description: 日志切面
 * @author 朱正磊
 * @date 2019-08-26 11:39:03
 */
@Slf4j
@Aspect
@Component
public class LogAspect {

    /**
     * description: 切点前执行
     * @author 朱正磊
     * @date 2019-08-26 13:21:28
     * @param point 切点（目标方法）
     */
    @Before("execution(* com.zzl.springboot_mybatis_demo.service.user.impl.*.*(..))")
    public void logBefore(JoinPoint point) {
        String className = point.getTarget().getClass().getName();
        System.out.println("【类全限定名】：" + className);

        String className2 = point.getSourceLocation().getWithinType().getName();
        System.out.println("【类全限定名】：" + className2);

        String methodName = point.getSignature().getName();
        System.out.println("【方法名】：" + methodName);

        log.info("【开启日志】...start...");
    }

    /**
     * description: 切点后执行
     * @author 朱正磊
     * @date 2019-08-26 13:22:04
     * @param point 切点（目标方法）
     */
    @After("execution(* com.zzl.springboot_mybatis_demo.service.user.impl.*.*(..))")
    public void logAfter(JoinPoint point) {
        Object[] args = point.getArgs();
        System.out.println("【实参】：" + Arrays.toString(args));

        Method method = ((MethodSignature) point.getSignature()).getMethod();
        String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(method);
        System.out.println("【形参】：" + ArrayUtil.array2String(parameterNames));

        String className = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();
        try {
            String[] paramsArgsName = this.getParamsArgsName(className, methodName);
            System.out.println("【形参】：" + ArrayUtil.array2String(paramsArgsName));
        } catch (Exception e) {
            e.printStackTrace();
        }

        log.info("【结束日志】...end...");
    }

    /**
     * description: 根据类全限定名和方法名获取方法形参集合
     * @author 朱正磊
     * @date 2019-08-26 13:24:09
     * @param className 类全限定名
     * @param methodName 方法名
     * @return 返回方法形参集合
     * @throws Exception 异常
     */
    private String[] getParamsArgsName(String className, String methodName) throws Exception {
        Class<?> clazz = Class.forName(className);
        //这里的 clazzName 和 className 其实是相同的
        //String clazzName = clazz.getName();
        ClassClassPath classPath = new ClassClassPath(clazz);
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(classPath);

        CtClass ctClass = pool.get(className);
        CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
        MethodInfo methodInfo = ctMethod.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            return null;
        }
        String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
        int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramsArgsName.length; i++) {
            paramsArgsName[i] = attr.variableName(i + pos);
        }
        return paramsArgsName;
    }

}
